﻿using System;
using System.Diagnostics.Contracts;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.Helpers;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;

namespace OpenWaves.ActivityFeed.NHibernate
{
    public class NHibernateSessionFactory: INHibernateSessionFactory
    {
        private const string TablesPrefix = "AF_";

        private readonly INHibernateSessionFactoryConfiguration _configuration;
        private readonly bool _recreateSchema;
        private readonly Action<Configuration> _configAction;

        protected INHibernateSessionFactoryConfiguration Configuration
        {
            get { return this._configuration; }
        }

        public NHibernateSessionFactory(): this(ServiceLocator.Resolve<INHibernateSessionFactoryConfiguration>(), false, null)
        {            
        }

        public NHibernateSessionFactory(INHibernateSessionFactoryConfiguration configuration): this(configuration, false, null)
        {            
        }

        public NHibernateSessionFactory(INHibernateSessionFactoryConfiguration configuration, bool recreateSchema): this(configuration, recreateSchema, null)
        {            
        }

        public NHibernateSessionFactory(INHibernateSessionFactoryConfiguration configuration, bool recreateSchema, Action<Configuration> configAction)
        {
            Contract.Requires<ArgumentNullException>(configuration != null);
            this._configuration = configuration;
            this._recreateSchema = recreateSchema;
            this._configAction = configAction;
        }

        public virtual global::NHibernate.ISessionFactory CreateSessionFactory()
        {                           
            return
                Fluently.Configure()                    
                        .Database(this.Configurer)                                                
                        .Mappings(m => m.FluentMappings.AddFromAssemblyOf<UpdateMap>().Conventions.Add(this.CreateConventions()))
                        .ExposeConfiguration(this.ExposeConfiguration)                        
                        .BuildSessionFactory();
        } 
       
        protected virtual IConvention[] CreateConventions()
        {
            return new IConvention[]
                {
                    ConventionBuilder.Class.Always(x => x.Table(String.Concat(TablesPrefix, x.EntityType.Name.ToUpper()))),
                    ConventionBuilder.HasMany.Always(x => x.Table(String.Concat(TablesPrefix, x.Member.Name.ToUpper()))),
                };
        }

        protected virtual IPersistenceConfigurer Configurer
        {
            get { return MsSqlConfiguration.MsSql2005.ShowSql().ConnectionString(this._configuration.ConnectionString); }
        }

        protected virtual void ExposeConfiguration(Configuration configuration)
        {
            configuration.AddIndexes();
            configuration.AddConstraints();
            if (_configAction != null)
            {
                this._configAction(configuration);
            }
            if (this._recreateSchema)
            {
                new SchemaExport(configuration).Execute(false, true, false);
            }
        }
    }
}